/* XXX Custom SO rule.  Below used for generation of base code only XXX
alert udp $EXTERNAL_NET 53 -> $HOME_NET any (msg:"BAD-TRAFFIC TMG Firewall Client long host entry exploit attempt"; flow:to_client; content:"|00 01|"; depth:2; offset:4; reference:cve,2011-1889; reference:url,technet.microsoft.com/en-us/security/bulletin/MS11-040; classtype:attempted-user; sid:19187;)
*/
/*
 * Vuln Title: XXXX
 *
 * Copyright (C) 2005-2010 Sourcefire, Inc. All Rights Reserved
 *
 * Written by XXXX, Sourcefire VRT <XXXX@sourcefire.com>
 *
 * Auto-generated by XXXX
 *
 * This file may contain proprietary rules that were created, tested and
 * certified by Sourcefire, Inc. (the "VRT Certified Rules") as well as
 * rules that were created by Sourcefire and other third parties and
 * distributed under the GNU General Public License (the "GPL Rules").  The
 * VRT Certified Rules contained in this file are the property of
 * Sourcefire, Inc. Copyright 2005 Sourcefire, Inc. All Rights Reserved.
 * The GPL Rules created by Sourcefire, Inc. are the property of
 * Sourcefire, Inc. Copyright 2002-2005 Sourcefire, Inc. All Rights
 * Reserved.  All other GPL Rules are owned and copyrighted by their
 * respective owners (please see www.snort.org/contributors for a list of
 * owners and their respective copyrights).  In order to determine what
 * rules are VRT Certified Rules or GPL Rules, please refer to the VRT
 * Certified Rules License Agreement.
 */

#include "sf_snort_plugin_api.h"
#include "sf_snort_packet.h"

#include "so-util.h"

//#define DEBUG
#ifdef DEBUG
#define DEBUG_SO(code) code
#else
#define DEBUG_SO(code)
#endif


/* declare detection functions */
int rule19187eval(void *p);

/* declare rule data structures */
/* flow:to_client; */
static FlowFlags rule19187flow0 = 
{
    FLOW_TO_CLIENT
};

static RuleOption rule19187option0 =
{
    OPTION_TYPE_FLOWFLAGS,
    {
        &rule19187flow0
    }
};
// content:"|00 01|", offset 4, depth 2, fast_pattern; 
static ContentInfo rule19187content1 = 
{
    (uint8_t *) "|00 01|", /* pattern (now in snort content format) */
    2, /* depth */
    4, /* offset */
    CONTENT_FAST_PATTERN|CONTENT_BUF_NORMALIZED, /* flags */
    NULL, /* holder for boyer/moore PTR */
    NULL, /* more holder info - byteform */
    0, /* byteform length */
    0 /* increment length*/
};

static RuleOption rule19187option1 = 
{
    OPTION_TYPE_CONTENT,
    {
        &rule19187content1
    }
};

/* references for sid 19187 */
/* reference: cve "2011-1889"; */
static RuleReference rule19187ref1 = 
{
    "cve", /* type */
    "2011-1889" /* value */
};

/* reference: url "technet.microsoft.com/en-us/security/bulletin/MS11-040"; */
static RuleReference rule19187ref2 = 
{
    "url", /* type */
    "technet.microsoft.com/en-us/security/bulletin/MS11-040" /* value */
};

static RuleReference *rule19187refs[] =
{
    &rule19187ref1,
    &rule19187ref2,
    NULL
};
/* metadata for sid 19187 */
/* metadata:; */

static RuleMetaData rule19187policy1 = 
{
   "policy max-detect-ips drop"
};

static RuleMetaData *rule19187metadata[] =
{
    &rule19187policy1,
    NULL
};

RuleOption *rule19187options[] =
{
    &rule19187option0,
    &rule19187option1,
    NULL
};

Rule rule19187 = {
   /* rule header, akin to => tcp any any -> any any */
   {
       IPPROTO_UDP, /* proto */
       "$EXTERNAL_NET", /* SRCIP     */
       "53", /* SRCPORT   */
       0, /* DIRECTION */
       "$HOME_NET", /* DSTIP     */
       "any", /* DSTPORT   */
   },
   /* metadata */
   { 
       3,  /* genid */
       19187, /* sigid */
       7, /* revision */
       "attempted-user", /* classification */
       0,  /* hardcoded priority XXX NOT PROVIDED BY GRAMMAR YET! */
       "PROTOCOL-DNS TMG Firewall Client long host entry exploit attempt",     /* message */
       rule19187refs /* ptr to references */
       ,rule19187metadata
   },
   rule19187options, /* ptr to rule options */
   &rule19187eval, /* use the built in detection function */
   0 /* am I initialized yet? */
};


/* detection functions */
int rule19187eval(void *p) {
   const uint8_t *cursor_normal = 0;
   SFSnortPacket *sp = (SFSnortPacket *) p;

   uint16_t flags, num_of_answers, data_len;
   const uint8_t *beg_of_payload, *end_of_payload;
   const uint8_t *start_hostent, *end_hostent;

   int i;

   if(sp == NULL)
       return RULE_NOMATCH;

   if(sp->payload == NULL)
       return RULE_NOMATCH;

   // flow:to_client;
   if (checkFlow(p, rule19187options[0]->option_u.flowFlags) > 0 ) {

      if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
         return RULE_NOMATCH;

      // Ensure we have enough data
      // txid, flags, num records * 4, query response (unk), at least 256 bytes of hostent
      // So, at least 300 bytes, I guess.
      DEBUG_SO(printf("Payload size = %d\n", (int)(end_of_payload - beg_of_payload));)
      if(end_of_payload - beg_of_payload < 100)
         return RULE_NOMATCH;

      // Forcing to be only one response entry
      // content:"|00 01|", offset 4, depth 2, fast_pattern;
      if (contentMatch(p, rule19187options[1]->option_u.content, &cursor_normal) <= 0) {
          return RULE_NOMATCH;
      }

      // Now, let's make sure this is actually a standard query response.
      // Our content match above ensures we have requisite data.

      flags = read_big_16(cursor_normal - 4);

      if((flags & 0xFA0F) != 0x8000) {
         // 1 bit - response (1 = response)
         // 4 bits - opcode (0000 = standard query)
         // 1 bit - authoritative (don't care)
         // 1 bit - truncated (0 = not truncated)
         // 5 bits - recursion and other don't cares
         // 4 bits - reply code (0000 = no error)
         return RULE_NOMATCH;
      }

      // Get number of answers
      num_of_answers = read_big_16_inc(cursor_normal);
      DEBUG_SO(printf("We have %d answers\n", num_of_answers);)

      // Don't care about number of authority or other RRs
      cursor_normal += 4; // 2 + 2 

      DEBUG_SO(printf("Skipping over query section\n");)

      // Now we are at the start of the query section.  We limit it to one query so jump over it
      while(cursor_normal < end_of_payload && *cursor_normal != 0 && *cursor_normal < 0xc0) { // !((*cursor_normal & 0xc0) == 0xc0)
         cursor_normal += *cursor_normal + 1;
      }

      if(cursor_normal >= end_of_payload)
         return RULE_NOMATCH;

      // (two bytes for pointer (2 bytes)  or null byte (1 byte)) + type (2 bytes) + class (2 bytes)
      cursor_normal += (*cursor_normal >= 0xc0) ? 6 : 5;  // (*cursor_normal & 0xc0) == 0xc0

      // We are now at the start of the answer section.  Store the pointer for later.
      start_hostent = cursor_normal;

      DEBUG_SO(printf("Starting to process the answer section\n");)

      // Now, go to the end of the answer section
      for(i = 0; i < num_of_answers; i++) {
         DEBUG_SO(printf("processing answer %d, payload_offset=%04x\n", i, (int)(cursor_normal - beg_of_payload)));
         while(cursor_normal < end_of_payload && *cursor_normal != 0 && *cursor_normal < 0xc0) { // performance improvement, !((*cursor_normal & 0xc0) == 0xc0)  
            cursor_normal += *cursor_normal + 1;
         }
   
         if(cursor_normal + 4 > end_of_payload)
            return RULE_NOMATCH;
   
         // (two bytes for pointer (2 bytes) or a single null byte (1 byte)) + type (2 bytes) + class (2 bytes) + ttl (4 bytes)
         cursor_normal += (*cursor_normal >= 0xc0) ? 2 : 1;

         // Skip record type (2 bytes), class type (2 bytes), and TTL (4 bytes)
         cursor_normal += 8;
   
         if(cursor_normal + 2 > end_of_payload)
            return RULE_NOMATCH;
   
         data_len = read_big_16_inc(cursor_normal);
   
         cursor_normal += data_len;
      }

      end_hostent = cursor_normal;

      // Calculate the total size of the host entry
      DEBUG_SO(printf("total size of host entry = %d\n", (int)(end_hostent - start_hostent));)

      if(end_hostent - start_hostent > 70) // Reduced limit from 256 due to sample that uses this shorter length
         return RULE_MATCH;

   }
   return RULE_NOMATCH;
}
/*
Rule *rules[] = {
    &rule19187,
    NULL
};
*/
